import base64
import logging
import traceback
import urllib.request
import time
import json
import uuid
from threading import Timer

from flask import jsonify
from dao.db_connect import db_connect as db

from alipay.aop.api.AlipayClientConfig import AlipayClientConfig
from alipay.aop.api.DefaultAlipayClient import DefaultAlipayClient
from alipay.aop.api.domain.AlipayTradePagePayModel import AlipayTradePagePayModel
from alipay.aop.api.request.AlipayTradePagePayRequest import AlipayTradePagePayRequest
from alipay.aop.api.domain.AlipayTradeQueryModel import AlipayTradeQueryModel
from alipay.aop.api.request.AlipayTradeQueryRequest import AlipayTradeQueryRequest
from alipay.aop.api.response.AlipayTradeQueryResponse import AlipayTradeQueryResponse
from alipay.aop.api.domain.AlipayTradeCloseModel import AlipayTradeCloseModel
from alipay.aop.api.request.AlipayTradeCloseRequest import AlipayTradeCloseRequest
import alipay.aop.api.util.SignatureUtils as SignatureUtils

import controller.carbon_alipay.AlipayConfig as AlipayConfig
from controller.carbon_alipay.Alipay_method import AliPayMethod
from controller.carbon_alipay.update_sys_status import UpdateStatus

# 请求成功
success = 200
identification_error = 100  # 未识别成功

# 订单中需要写入数据库的字段
# 有些字段不是必选项，因此其数值可能为 None
order_keys = [
    "trade_no",
    "trade_status",
    "buyer_id",
    "seller_id",
    "total_amount",
    "receipt_amount",
    "invoice_amount",
    "buyer_pay_amount",
    "refund_fee",
    "gmt_create",
    "gmt_payment",
    "gmt_refund",
    "gmt_close",
]

status_dic = {
    "WAIT_BUYER_PAY": AlipayConfig.WAIT_BUYER_PAY,
    "TRADE_CLOSED": AlipayConfig.TRADE_CLOSED,
    "TRADE_SUCCESS": AlipayConfig.TRADE_SUCCESS,
    "TRADE_FINISHED": AlipayConfig.TRADE_FINISHED,
}


def get_uuid():
    return "".join(str(uuid.uuid4()).split("-")).lower()


class AliPay(object):
    @classmethod
    def pay_test(cls, request):
        """
        进行网站订单支付测试
        """
        logging.debug("working on alipay paying......")

        try:
            good_id = '1E4166ACE2124953811EDB6026CF772A'
            return_url = AlipayConfig.return_url
            query_sql = f'select good_id, good_name, good_describe, good_price from goods where good_id = "{good_id}"'
            good = db.queryOne(query_sql)
            if not good:
                logging.debug("当前商品不存在！")
                return None, None

            # 订单价格 & 标题 & 商品描述
            total_amount = float(good['good_price']) / 100
            subject = "购买-" + good['good_name']
            body = good['good_describe']
            qr_pay_mode = 0  # 订单码-前置模式

            return_map, response = AliPayMethod.create_order(
                return_url, total_amount, subject, body, qr_pay_mode)

            if response is None:
                logging.debug("创建订单失败！")
                return None, None

            out_trade_no = return_map.get('out_trade_no')
            app_id = return_map.get('app_id')
            time_create = return_map.get('time_create')
            time_expire = return_map.get('time_expire')

            # 将订单入库
            insert_sql = f"insert into orders (app_id, out_trade_no, user_id, good_id, " \
                         f"subject, total_amount, body, gmt_close, gmt_create) " \
                         f"values ('{app_id}', '{out_trade_no}', 'EA2A1BE4932D46A4AEBCF879986951B4', " \
                         f"'1E4166ACE2124953811EDB6026CF772A', '{subject}', " \
                         f"'{int(good['good_price'])}', '{body}', '{time_expire}', '{time_create}')"
            db.UpdateOrDeleteOrInsert(insert_sql)
        except Exception as e:
            logging.debug(traceback.format_exc())
            return None, None

        # 启动 Timer 进行订单状态的查询
        query_kwargs = {
            "out_trade_no": out_trade_no,
            "trade_no": None
        }
        query_timer = Timer(AlipayConfig.time_expire + 120, AliPay.query, args=None, kwargs=query_kwargs)
        query_timer.start()
        return out_trade_no, response

    @classmethod
    def pay(cls, return_url, good_id, user_id):
        """
        进行网站订单支付
        """
        logging.debug("working on alipay paying......")

        try:
            query_sql = f'select good_id, good_name, good_describe, good_price from goods where good_id = "{good_id}"'
            good = db.queryOne(query_sql)
            if not good:
                logging.debug("当前商品不存在！")
                return None, None

            # 订单价格 & 标题 & 商品描述
            total_amount = float(good['good_price']) / 100
            subject = "购买-" + good['good_name']
            body = good['good_describe']
            qr_pay_mode = 4  # 订单码-前置模式
            qrcode_width = 100

            return_map, response = AliPayMethod.create_order(
                return_url, total_amount, subject, body, qr_pay_mode, qrcode_width)

            if response is None:
                logging.debug("创建订单失败！")
                return None, None

            out_trade_no = return_map.get('out_trade_no')
            app_id = return_map.get('app_id')
            time_create = return_map.get('time_create')
            time_expire = return_map.get('time_expire')

            # 将订单入库
            insert_sql = f"insert into orders (app_id, out_trade_no, user_id, good_id, " \
                         f"subject, total_amount, body, gmt_close, gmt_create) " \
                         f"values ('{app_id}', '{out_trade_no}', '{user_id}', '{good_id}', '{subject}', " \
                         f"'{int(good['good_price'])}', '{body}', '{time_expire}', '{time_create}')"
            db.UpdateOrDeleteOrInsert(insert_sql)
        except Exception as e:
            logging.debug(traceback.format_exc())
            return None, None

        # 启动 Timer 进行订单状态的查询
        query_kwargs = {
            "out_trade_no": out_trade_no,
            "trade_no": None
        }
        query_timer = Timer(AlipayConfig.time_expire + 120, AliPay.query, args=None, kwargs=query_kwargs)
        query_timer.start()
        return out_trade_no, response

    @classmethod
    def update_status_after_success(cls, good_id, user_id):
        """
        更新其他状态，该部分由于购买得到商品种类(good_label)不同，
           需要进行不同的更新，其中需要注意的点如下：
           1. 不要重复更新系统的状态
           2. 对于购买会员的，需要更新会员状态，这些状态包括：
               · 修改会员等级
               · 刷新vip过期时间
           3. 对于购买文件的，需要更新文件购买表
               · 增加新的购买记录
        :param good_id:
        :param user_id:
        :return:
        """
        try:
            query_sql = f'select good_id, good_name, good_label from goods where good_id = "{good_id}"'
            good = db.queryOne(query_sql)
        except Exception as e:
            logging.debug("商品信息查询失败！")
            return False

        # 根据商品的特性修改系统的状态
        if good['good_label'] == 'vip':
            # 修改用户的会员状态
            good_name = good['good_name']
            vip_name_to_time = {
                "1年": 365,
                "3个月": 90,
                "1个月": 30,
            }
            vip_time = vip_name_to_time[good_name] * 24 * 3600
            result = UpdateStatus.update_user_after_paying(user_id, vip_time)
            if result is False:
                logging.error(f"订单用户信息更新失败，会员信息出现异常！")
                return False

        if good['good_label'] == 'file':
            purchase_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time()))
            result = UpdateStatus.update_files_purchased_after_paying(user_id, good_id, purchase_time)
            if result is False:
                return False

        return True

    @classmethod
    def notify(cls, request):
        """
        进行异步验证签名
        """
        logging.debug("working on alipay notifying......")

        verify_result = AliPayMethod.verify_signature(request)
        if not verify_result:
            return jsonify('failure')

        # 验证 app_id 是否为该商户本身
        app_id = request.values.get('app_id')
        if app_id is None or app_id != AlipayConfig.appid:
            logging.debug("app_id验证失败！")
            return jsonify('failure')

        # 取出对应的订单号
        # 验证该通知数据中的 out_trade_no 是否为商户系统中创建的订单号
        out_trade_no = request.values.get('out_trade_no')
        if out_trade_no is None:
            logging.debug("out_trade_no参数丢失！")
            return jsonify('failure')
        try:
            query_sql = f"select * from orders where out_trade_no ='{out_trade_no}' and app_id = '{app_id}'"
            order_info = db.queryOne(query_sql)
        except Exception as e:
            logging.debug("订单查询失败，数据库中不存在该订单！")
            return jsonify('failure')

        if order_info is None:
            logging.debug("订单查询失败，数据库中不存在该订单！")
            return jsonify('failure')

        # 判断 total_amount 是否确实为该订单的实际金额
        total_amount_db = order_info.get("total_amount")
        total_amount_re = int(float(request.values.get("total_amount")) * 100)

        if total_amount_db != total_amount_re:
            logging.debug("订单金额不符合！")
            return jsonify('failure')

        # 校验通知中的 seller_id（或者 seller_email) 是否为 out_trade_no 这笔单据的对应的操作方
        # （有的时候，一个商户可能有多个 seller_id/seller_email）
        # 暂时不进行验证

        # 更新订单状态
        try:
            update_order = "update orders set "
            for key in order_keys:
                order_value = request.values.get(key)
                # print(key + ":" + str(order_value))
                if order_value is None:
                    continue
                if key == 'trade_status':
                    order_value = status_dic[order_value]
                    update_order += f"{key} = {order_value} , "
                elif key in ("total_amount", "receipt_amount", "invoice_amount", "buyer_pay_amount", "refund_fee"):
                    order_value = int(float(request.values.get("total_amount")) * 100)
                    update_order += f"{key} = {order_value} , "
                else:
                    update_order += f"{key} = '{order_value}' , "

            update_order = update_order[0: -2]
            update_order += f" where out_trade_no = '{out_trade_no}' and app_id = '{app_id}'"
            logging.debug("update sql:" + update_order)
            db.UpdateOrDeleteOrInsert(update_order)
        except Exception as e:
            return None

        # 验证交易通知状态是否为 TRADE_SUCCESS 或 TRADE_FINISHED
        trade_status = request.values.get('trade_status')
        logging.debug("交易通知状态:" + str(trade_status))
        if trade_status is None or trade_status not in ("TRADE_SUCCESS", "TRADE_FINISHED"):
            return jsonify('failure')

        # 如果之前已经更新完成了，不再次修改系统状态
        if order_info['trade_status'] in (AlipayConfig.TRADE_SUCCESS, AlipayConfig.TRADE_FINISHED):
            return jsonify('success')

        '''
        更新其他状态，该部分由于购买得到商品种类(good_label)不同，
            需要进行不同的更新，其中需要注意的点如下：
            1. 不要重复更新系统的状态
            2. 对于购买会员的，需要更新会员状态，这些状态包括：
                · 修改会员等级
                · 刷新vip过期时间
            3. 对于购买文件的，需要更新文件购买表
                · 增加新的购买记录 
        '''
        try:
            # 查询商品
            good_id = order_info.get('good_id')
            if good_id is None:
                logging.debug("重新查询订单！")
                query_sql = f"select * from orders where out_trade_no ='{out_trade_no}' and app_id = '{app_id}'"
                order_info = db.queryOne(query_sql)
                if order_info is None:
                    logging.debug("订单信息查询失败！")
                    return jsonify('failure')
        except Exception as e:
            logging.debug("商品信息查询失败！")
            resp = 'failure'
            return jsonify(resp)

        user_id = order_info['user_id']
        result = cls.update_status_after_success(good_id, user_id)
        if result is False:
            return jsonify('failure')
        return jsonify('success')

    @classmethod
    def query(cls, out_trade_no=None, trade_no=None):
        """
        进行网站订单查询
        :param out_trade_no: 商家商品订单号
        :param trade_no: 支付宝支付订单号
        :return
            code: 100 TRADE_SUCCESS
                101 TRADE_FINISHED
                102 WAIT_BUYER_PAY
                103 TRADE_CLOSED
                104 ACQ.TRADE_NOT_EXIST
                105 out_trade_no & trade_no 同时为空
                106 数据库订单丢失
                107 数据库数据异常
                108 其他支付宝查询异常
                109 订单金额不符合
                110 系统状态更新失败
        """
        logging.debug("working on alipay querying......")
        logging.debug("out_trade_no：" + str(out_trade_no) + ", trade_no：" + str(trade_no))

        # 商户订单号 & 支付宝交易号，两者不能同时为空
        if out_trade_no is None and trade_no is None:
            return 105

        # 查询数据库中是否存在该订单
        query_sql = f"select app_id, out_trade_no, user_id, good_id, trade_status, total_amount, gmt_close from orders " \
                    f"where out_trade_no='{out_trade_no}' and app_id='{AlipayConfig.appid}'"
        order_info = db.queryOne(query_sql)
        if order_info is None:
            return 106

        response = AliPayMethod.query_order(out_trade_no, trade_no)
        code = response.get('code')
        sub_code = response.get('sub_code')

        # 格式化时间转时间戳
        gmt_close = order_info.get('gmt_close')
        if gmt_close is None:
            logging.error("数据库中不存在")
            return 107
        gmt_close = time.mktime(time.strptime(str(gmt_close), "%Y-%m-%d %H:%M:%S"))

        # 请求异常
        if code is None or code != '10000':
            logging.debug("订单查询异常：" + str(sub_code))
            if code == '40004' and sub_code == 'ACQ.TRADE_NOT_EXIST':
                # 订单不存在且已经关闭，删除订单
                if gmt_close < int(time.time()):
                    delete_order = f"delete from orders where out_trade_no='{out_trade_no}' "
                    logging.debug("delete sql:" + delete_order)
                    db.UpdateOrDeleteOrInsert(delete_order)
                    return 103
                return 104
            return 108

        buyer_id = response.get('buyer_pay_amount')
        invoice_amount = response.get('invoice_amount')
        receipt_amount = response.get('receipt_amount')
        buyer_pay_amount = response.get('buyer_pay_amount')
        total_amount = response.get('total_amount')
        trade_status = response.get('trade_status')
        out_trade_no = response.get('out_trade_no')
        trade_no = response.get('trade_no')

        # 判断 total_amount 是否确实为该订单的实际金额
        total_amount_db = order_info.get("total_amount")
        total_amount_re = int(float(total_amount) * 100)

        if total_amount_db != total_amount_re:
            logging.debug("订单金额不符合！")
            return 109

        if trade_status == 'WAIT_BUYER_PAY':
            return 102

        # 更新订单状态
        try:
            update_order = f"update orders set " \
                           f"trade_no='{trade_no}', trade_status={status_dic[trade_status]} " \
                           f"where out_trade_no='{out_trade_no}' and app_id='{AlipayConfig.appid}'"
            logging.debug("update sql:" + update_order)
            db.UpdateOrDeleteOrInsert(update_order)
        except Exception as e:
            return 107

        # 验证交易通知状态是否为 TRADE_SUCCESS 或 TRADE_FINISHED
        if trade_status is None or trade_status not in ("TRADE_SUCCESS", "TRADE_FINISHED"):
            return 103

        # 如果之前已经更新完成了，不再次修改系统状态
        if order_info['trade_status'] in (AlipayConfig.TRADE_SUCCESS, AlipayConfig.TRADE_FINISHED):
            return 100

        good_id = order_info['good_id']
        user_id = order_info['user_id']
        result = cls.update_status_after_success(good_id, user_id)
        if result is False:
            return 110
        return 100
